package com.dps.recommend.process;

import com.alibaba.fastjson.JSON;
import com.dps.metrics.annotation.CustomizedMetrics;
import com.dps.metrics.processor.CustomizedMetricsProcessor;
import com.dps.recommend.ann.LayerAnn;
import com.dps.recommend.beans.*;
import com.dps.recommend.config.DpsApolloRefreshConfig;
import com.dps.recommend.enums.DpsAbLoadEnum;
import com.dps.recommend.enums.LayerEnum;
import com.dps.recommend.metrics.DpsLayerTagMapper;
import com.dps.recommend.metrics.DpsRoadTagMapper;
import com.dps.recommend.metrics.DpsSenceTagMapper;
import com.dps.recommend.utils.IdGenerate;
import com.dps.recommend.utils.LotteryUtil;
import com.google.common.annotations.GwtIncompatible;
import com.google.common.base.Stopwatch;
import com.google.common.collect.Lists;
import lombok.extern.slf4j.Slf4j;
import org.slf4j.MDC;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import java.io.*;
import java.lang.reflect.Field;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.function.Supplier;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

@Component
@Slf4j(topic = "dpsRecLog")
public class DpsProcessor {

    private static Pattern p = Pattern.compile("\t|\r|\n");

    @Autowired(required = false)
    private Map<String, ProcessInterface> processInterfaceMap;

    @Autowired
    @Qualifier("taskDpsExecutor")
    private ExecutorService taskExecutor;

    @Autowired
    private DpsProcessor dpsProcessor;

    @Autowired
    private DpsApolloRefreshConfig dpsApolloRefreshConfig;


    /**
     * 处理requestId
     *
     * @param recommendContext
     */
    public void dealRequestId(DpsRequest recommendContext) {
        String requestId = recommendContext.getRequest().getRequestId();
        recommendContext.getRequest().setRequestId(StringUtils.isEmpty(requestId) ? IdGenerate.nextId() : requestId);
        if (StringUtils.isEmpty(MDC.get("requestId"))) {
            MDC.put("requestId", recommendContext.getRequest().getRequestId());
        }

        Integer pageNum = recommendContext.getRequest().getPageNum();
        Integer pageSize = recommendContext.getRequest().getPageSize();
        if (Objects.isNull(pageNum) || pageNum == 0) {
            recommendContext.getRequest().setPageNum(1);
        }

        if (Objects.isNull(pageSize) || pageSize == 0) {
            recommendContext.getRequest().setPageSize(10);
        }
    }


    /**
     * 获取场景配置
     *
     * @param sence
     * @return
     */
    private DpsSence getSenceConfig(String sence) {
        DpsSence dpsSence = dpsApolloRefreshConfig.allHashMap.get(sence);
        if (Objects.isNull(dpsSence) || CollectionUtils.isEmpty(dpsSence.getGroupList())) {
            return null;
        }

        DpsSence dpsNewSence = new DpsSence();
        List<Group> groups = new ArrayList<>();
        dpsSence.getGroupList().forEach(group -> {
            Group groupNew = new Group();
            BeanUtils.copyProperties(group, groupNew);
            groups.add(groupNew);
        });
        dpsNewSence.setDpsAbLoad(dpsSence.getDpsAbLoad());
        dpsNewSence.setGroupList(groups);
        dpsNewSence.setDpsSenceTime(dpsSence.getDpsSenceTime());
        return dpsNewSence;

    }


    /**
     * 执行推荐策略
     *
     * @param recommendContext
     */
    @CustomizedMetrics(customPrefix = "dps_sence", tagMapper = DpsSenceTagMapper.class)
    public List process(DpsRequest recommendContext) {
        List result = dpsProcessor.processSence(recommendContext);
        this.recordCount(recommendContext, result);
        return result;
    }

    /**
     * 记录埋点
     *
     * @param dpsRequest
     * @param result
     */
    private void recordCount(DpsRequest dpsRequest, List result) {
        if (CollectionUtils.isEmpty(result)) {
            return;
        }
        List<RecItemDps> list = (List) result;
        //cn埋点
        Map<String, List<RecItemDps>> cnMapList = list.stream()
                .filter(rec -> !StringUtils.isEmpty(rec.getCn()))
                .collect(Collectors.groupingBy(RecItemDps::getCn));
        if (!org.springframework.util.CollectionUtils.isEmpty(cnMapList)) {
            String sence = StringUtils.isEmpty(dpsRequest.getRequest().getSence()) ? "" : dpsRequest.getRequest().getSence();
            String groupName = StringUtils.isEmpty(dpsRequest.getGroupName()) ? "" : dpsRequest.getGroupName();
            for (Map.Entry<String, List<RecItemDps>> entry : cnMapList.entrySet()) {
                String cnName = StringUtils.isEmpty(entry.getKey()) ? "" : entry.getKey();
                int cnSize = entry.getValue().size();
                CustomizedMetricsProcessor.recordCount("dps_sence_cn", null, cnSize, "sence", sence, "groupName", groupName, "cnName", cnName);
            }
        }

    }

    /**
     * 执行场景策略
     *
     * @param recommendContext
     * @return
     */
    public List processSence(DpsRequest recommendContext) {
        Stopwatch timer = Stopwatch.createStarted();
        DpsSence dpsSence = null;
        try {
            String deviceId = recommendContext.getRequest().getDeviceId();
            if (StringUtils.isEmpty(deviceId)) {
                log.info("userId:{},deviceId is empty", recommendContext.getRequest().getUserId());
                return Lists.newArrayList();
            }
            MDC.put("deviceId", deviceId);
            dpsProcessor.dealRequestId(recommendContext);
            if (Objects.isNull(recommendContext.getRequest()) || StringUtils.isEmpty(recommendContext.getRequest().getSence())) {
                return Lists.newArrayList();
            }
            String sence = recommendContext.getRequest().getSence();
            log.info("userId:{},sence:{},reqeust data:{}", recommendContext.getRequest().getUserId(), sence, JSON.toJSONString(recommendContext));
            dpsSence = getSenceConfig(sence);
            if (Objects.isNull(dpsSence)) {
                return Lists.newArrayList();
            }
            return dpsProcessor.doProcess(recommendContext, dpsSence);
        } finally {
            timer.stop();
            long end = timer.elapsed(TimeUnit.MILLISECONDS);
            log.info("userId:{},sence:{},time:{} ms", recommendContext.getRequest().getUserId(), recommendContext.getRequest().getSence(), end);
            long time = 300;
            if (!Objects.isNull(dpsSence) && !StringUtils.isEmpty(dpsSence.getDpsSenceTime())) {
                time = Long.valueOf(dpsSence.getDpsSenceTime());
            }
            if (end >= time) {
                log.info("userId:{},sence:{},time:{} too long", recommendContext.getRequest().getUserId(), recommendContext.getRequest().getSence(), end);
            }
            MDC.remove("deviceId");
        }
    }


    /**
     * 获取具体的组
     *
     * @param recommendContext
     * @param groupList
     * @param userId
     * @return
     */
    private Group getCurrentGroup(DpsRequest recommendContext, DpsSence dpsSence, String userId) {
        int tempGroupIndex = 0;
        List<Group> groupList = dpsSence.getGroupList();
        Map<String, Integer> groupIndexMap = new HashMap<>();
        try {
            List<Group> groups = dpsSence.getGroupList();
            dealABTestAndWhiteId(recommendContext, groups, dpsSence, userId, groupIndexMap);
            Integer groupIndex = groupIndexMap.getOrDefault("groupIndex", null);
            return groupList.get(Objects.isNull(groupIndex) ? tempGroupIndex : groupIndex);//获取组
        } catch (Exception e) {
        }
        return groupList.get(tempGroupIndex);//获取组;
    }


    /**
     * 执行组
     *
     * @param recommendContext
     * @param groupList
     * @return
     */
    public List doProcess(DpsRequest recommendContext, DpsSence dpsSence) {
        String userId = recommendContext.getRequest().getUserId();
        Group group = getCurrentGroup(recommendContext, dpsSence, userId);
        List result = dpsProcessor.doProcessGroup(recommendContext, group);
        recordSenceGroup(recommendContext, group, result);
        return result;
    }


    private void recordSenceGroup(DpsRequest recommendContext, Group group, List result) {
        String sence = recommendContext.getRequest().getSence();
        String groupName = StringUtils.isEmpty(group.getGroupName()) ? "" : group.getGroupName();
        CustomizedMetricsProcessor.recordCount("dps_sence_group", null, 1L, "sence", sence, "group", groupName);
        //CustomizedMetricsProcessor.recordGauge("dps_sence_group_num", null, CollectionUtils.isEmpty(result) ? 0 : result.size(), "sence", sence, "group", groupName);
    }

    /**
     * 执行组下的每一层
     *
     * @param recommendContext
     * @param group
     * @return
     */
    public List doProcessGroup(DpsRequest recommendContext, Group group) {
        Map<String, Object>[] extMap = new Map[]{new HashMap<>()};
        List[] list = {new ArrayList()};
        dealRecGroupContext(recommendContext, group);
        Arrays.stream(LayerEnum.values()).forEach(layerEnum -> {
            DpsRequest dpsRequest = new DpsRequest(recommendContext.getRequest());
            dealRecGroupContext(dpsRequest, group);
            dpsRequest.getExtMap().putAll(extMap[0]);
            dpsProcessor.doLayerProcess(dpsRequest, layerEnum, list, group);
            extMap[0] = dpsRequest.getExtMap();
        });
        return list[0];
    }


    private void dealRecGroupContext(DpsRequest recommendContext, Group group) {
        recommendContext.setGroupName(group.getGroupName());
        recommendContext.setGroupIsWhiteTest(group.getDpsWhiteSwitch());
        recommendContext.setGroupWeight(group.getDpsWeight());
        recommendContext.setGroupWhiteId(group.getDpsWhiteId());
    }


    @CustomizedMetrics(customPrefix = "dps_sence_layer", tagMapper = DpsLayerTagMapper.class)
    public void doLayerProcess(DpsRequest recommendContext, LayerEnum layerEnum, List[] list, Group group) {
        try {
            Layer layer = getFieldValueByFieldName(layerEnum.getCode(), group);
            if (Objects.isNull(layer)) {
                log.warn("userId:{},sence:{},group:{},layer:{} not exits", recommendContext.getRequest().getUserId(), recommendContext.getRequest().getSence(), group.getGroupName(), layerEnum.getCode());
                return;
            }
            List<List<Road>> handleRuleList = this.dealLayerRule(recommendContext, layer);
            list[0] = this.doProcess(recommendContext, handleRuleList, list[0]);
            if (CollectionUtils.isEmpty(list[0])) {
                list[0] = Lists.newArrayList();
            } else {
                List result = (List) list[0].stream().filter(Objects::nonNull).collect(Collectors.toList());
                list[0] = CollectionUtils.isEmpty(result) ? Lists.newArrayList() : result;
            }
        } catch (Exception e) {
            log.error("userId:{},sence:{},layer:{} error:{}", recommendContext.getRequest().getUserId(), recommendContext.getRequest().getSence(), layerEnum.getCode(), getStackTraceAsString(e));
        }
    }


    /**
     * 判断是否需要ab分流
     * 规则：自定义组group-->组的实验白名单IsWhiteTest(指定用户分发,其他用户该组不参与流量)-->白名单whiteId(指定用户分发,其他用户走ab)-->ab分流-->兜底default基准组
     *
     * @param recommendContext
     * @param groups
     * @param userId
     * @return
     */
    private void dealABTestAndWhiteId(DpsRequest recommendContext, List<Group> groups, DpsSence dpsSence, String userId, Map<String, Integer> groupIndexMap) {

        //自定义组group
        if (this.getGroupRule(recommendContext, groups, userId, groupIndexMap)) {
            return;
        }

        //组的实验白名单IsWhiteTest,开启后,指定用户走,其他不参与流量
        if (this.getIsWhiteTestRule(groups, recommendContext, userId, groupIndexMap)) {
            return;
        }

        //白名单whiteId
        if (this.getWhiteRule(groups, recommendContext, userId, groupIndexMap)) {
            return;
        }
        //走ab分流 多种策略
        if (DpsAbLoadEnum.weight.code.equalsIgnoreCase(dpsSence.getDpsAbLoad())) {
            this.getAbGroupRuleWeight(groups, recommendContext, userId, groupIndexMap);
        } else {
            this.getAbGroupRuleWeightPartition(groups, recommendContext, userId, groupIndexMap);
        }
    }


    /**
     * 组的实验白名单IsWhiteTest
     *
     * @param groups
     * @param userId
     * @return
     */
    private Boolean getIsWhiteTestRule(List<Group> groups, DpsRequest dpsRequest, String userId, Map<String, Integer> groupIndexMap) {
        boolean flage = Boolean.FALSE;
        List<Integer> indexList = new ArrayList<>();
        for (int i = 0; i < groups.size(); i++) {
            String whiteId = groups.get(i).getDpsWhiteId();
            String dpsWhiteSwitch = groups.get(i).getDpsWhiteSwitch();
            if (!StringUtils.isEmpty(dpsWhiteSwitch) && "true".equalsIgnoreCase(dpsWhiteSwitch) && !StringUtils.isEmpty(whiteId) && (Arrays.asList(whiteId.split(",")).contains(userId) || Arrays.asList(whiteId.split(",")).contains(dpsRequest.getRequest().getDeviceId()))) {
                groupIndexMap.put("groupIndex", i);
                log.info("userId:{},sence:{},groupName:{}", userId, dpsRequest.getRequest().getSence(), groups.get(i).getGroupName());
                flage = Boolean.TRUE;
                break;
            }
            if ("true".equalsIgnoreCase(dpsWhiteSwitch)) {
                //记录实验组的下标
                indexList.add(i);
            }
        }
        if (!flage) {
            //剔除isWhiteTest=true,但是没有配置白名单的组
            if (!CollectionUtils.isEmpty(groups) && groups.size() > indexList.size()) {
                indexList.forEach(index -> {
                    groups.remove((int) index);
                });
            }
        }
        return flage;
    }

    /**
     * ab 组级别：分流 按照权重分区分组
     *
     * @param groups
     * @param userId
     */
    private void getAbGroupRuleWeight(List<Group> groups, DpsRequest dpsRequest, String userId, Map<String, Integer> groupIndexMap) {
        List<Double> orignalRates = groups.stream().map(rec -> Double.valueOf(rec.getDpsWeight())).collect(Collectors.toList());
        Integer groupIndex = LotteryUtil.lottery(orignalRates);
        groupIndexMap.put("groupIndex", groupIndex);
        log.info("userId:{},sence:{},groupName:{}", userId, dpsRequest.getRequest().getSence(), groups.get(groupIndex).getGroupName());

    }


    /**
     * ab 组级别：分流 按照权重分区分组
     *
     * @param groups
     * @param userId
     */
    private void getAbGroupRuleWeightPartition(List<Group> groups, DpsRequest dpsRequest, String userId, Map<String, Integer> groupIndexMap) {
        String id = StringUtils.isEmpty(dpsRequest.getRequest().getDeviceId()) ? userId : dpsRequest.getRequest().getDeviceId();
        int idHash = Math.abs(id.hashCode()) % 100;
        log.info("userId:{},sence:{},id:{} idHash:{}", userId, dpsRequest.getRequest().getSence(), id, idHash);
        for (int i = 0; i < groups.size(); i++) {
            String weightPartition = groups.get(i).getDpsWeight();
            String[] flows = weightPartition.split(",");
            for (int j = 0; j < flows.length; j++) {
                try {
                    String flow = flows[j];
                    String[] configFlow = flow.split("\\-");
                    if (configFlow.length < 2 || StringUtils.isEmpty(configFlow[0]) || StringUtils.isEmpty(configFlow[1])) {
                        continue;
                    }
                    Integer flowBefore = Integer.valueOf(configFlow[0]);
                    Integer flowEnd = Integer.valueOf(configFlow[1]);
                    if (idHash >= flowBefore && idHash < flowEnd) {
                        groupIndexMap.put("groupIndex", i);
                        log.info("userId:{},sence:{},groupName:{}", userId, dpsRequest.getRequest().getSence(), groups.get(i).getGroupName());
                        return;
                    }
                } catch (Exception e) {

                }
            }
        }
    }

    /**
     * ab 路级别：分流 按照权重分配
     *
     * @param groups
     * @param userId
     */
    private Road getAbRoadRule(List<Road> roads) {
        int groupRoadIndex = 0;
        try {
            List<Double> orignalRates = roads.stream().map(Road::getDpsWeight).collect(Collectors.toList());
            return roads.get(LotteryUtil.lottery(orignalRates));
        } catch (Exception e) {
        }
        return roads.get(groupRoadIndex);
    }


    /**
     * 获取白名单路由规则
     *
     * @param groups
     * @param userId
     * @return
     */
    private Boolean getWhiteRule(List<Group> groups, DpsRequest dpsRequest, String userId, Map<String, Integer> groupIndexMap) {
        for (int i = 0; i < groups.size(); i++) {
            String whiteId = groups.get(i).getDpsWhiteId();
            if (!StringUtils.isEmpty(whiteId) && (Arrays.asList(whiteId.split(",")).contains(dpsRequest.getRequest().getDeviceId()) || Arrays.asList(whiteId.split(",")).contains(userId))) {
                groupIndexMap.put("groupIndex", i);
                log.info("userId:{},sence:{},groupName:{}", userId, dpsRequest.getRequest().getSence(), groups.get(i).getGroupName());
                return Boolean.TRUE;
            }
        }
        return Boolean.FALSE;
    }

    /**
     * 优先组的规则
     *
     * @param recommendContext
     * @param groups
     * @param userId
     * @return
     */
    private Boolean getGroupRule(DpsRequest dpsRequest, List<Group> groups, String userId, Map<String, Integer> groupIndexMap) {
        boolean flage = Boolean.FALSE;
        String group = dpsRequest.getRequest().getGroup();
        if (StringUtils.isEmpty(group)) {
            return flage;
        }

        for (int i = 0; i < groups.size(); i++) {
            if (group.equalsIgnoreCase(groups.get(i).getGroupName())) {
                groupIndexMap.put("groupIndex", i);
                log.info("userId:{},sence:{},groupName:{}", userId, dpsRequest.getRequest().getSence(), groups.get(i).getGroupName());
                return Boolean.TRUE;
            }
        }

        return flage;
    }


    private Layer getFieldValueByFieldName(String fieldName, Object object) {
        try {
            Field field = object.getClass().getDeclaredField(fieldName);
            field.setAccessible(true);
            return (Layer) field.get(object);
        } catch (Exception e) {
            return null;
        }
    }

    /**
     * 筛选符合场景规则的策略
     *
     * @param recommendContext
     * @param handleList
     * @return
     */
    private List<List<Road>> getExistModeName(DpsRequest recommendContext, List<List<Road>> handleList) {
        List<List<Road>> roadList = new ArrayList<>();
        handleList.forEach(roads -> {
            roads = roads.stream()
                    .filter(handle -> {
                        if (!Objects.isNull(processInterfaceMap.getOrDefault(handle.getModel(), null))) {
                            return Boolean.TRUE;
                        }
                        log.warn("userId:{},sence:{},current layer is {},error model:{},modelName:{} not exist", recommendContext.getRequest().getUserId(), recommendContext.getRequest().getSence(), recommendContext.getLayer().getLayerName(), handle.getModel(), handle.getModelName());
                        return Boolean.FALSE;
                    }).filter(invok -> {
                        String model = invok.getModel();
                        String modelName = invok.getModelName();
                        ProcessInterface processInterface = processInterfaceMap.get(model);
                        LayerAnn layerAnn = processInterface.getClass().getAnnotation(LayerAnn.class);
                        if (Objects.isNull(layerAnn) || !layerAnn.layer().code.equalsIgnoreCase(recommendContext.getLayer().getLayerName())) {
                            log.warn("userId:{},sence:{},current layer:{},error layer:{},model:{},modelName:{}", recommendContext.getRequest().getUserId(), recommendContext.getRequest().getSence(), recommendContext.getLayer().getLayerName(), layerAnn.layer(), model, modelName);
                            return Boolean.FALSE;
                        }
                        return Boolean.TRUE;
                    }).collect(Collectors.toList());
            if (!CollectionUtils.isEmpty(roads)) {
                roadList.add(roads);
            }

        });
        return roadList;
    }

    /**
     * 转换策略实体
     *
     * @param recommendContext
     * @param invok
     * @return
     */
    private DpsRequest convertProcess(DpsRequest recommendContext, Road road) {

        DpsRequest dpsRequest = null;
        try {
            dpsRequest = deepClone(recommendContext);
            Road roadTmp = new Road();
            BeanUtils.copyProperties(road, roadTmp);
            dpsRequest.setRoad(roadTmp);
        } catch (Exception e) {
            log.error("convertProcess error:{}", getStackTraceAsString(e));
        }

        return dpsRequest;
    }


    public <T> T deepClone(T obj) throws IOException, ClassNotFoundException {

        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        ObjectOutputStream oos = new ObjectOutputStream(bos);
        oos.writeObject(obj);
        // 将流序列化成对象
        ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
        ObjectInputStream ois = new ObjectInputStream(bis);
        return (T) ois.readObject();
    }

    /**
     * 获取分组超时时间
     *
     * @param recommendContext
     * @return
     */
    private long getTimeOut(DpsRequest recommendContext) {
        return Long.valueOf(String.valueOf(recommendContext.getLayer().getDpsTimeOut()));
    }

    private List<Road> getRoadList(List<List<Road>> invokList) {
        List<Road> roadList = new ArrayList<>();
        invokList.forEach(invok -> {
            if (CollectionUtils.isEmpty(invok)) {
                return;
            }
            roadList.add(this.getAbRoadRule(invok));
        });

        return roadList;
    }

    /**
     * 循环每层策略
     *
     * @param recommendContext
     * @param handleRuleList
     * @param layerResultList
     * @return
     */
    private List doProcess(DpsRequest recommendContext, List<List<Road>> handleRuleList, List layerResultList) {
        if (CollectionUtils.isEmpty(handleRuleList)) {
            return layerResultList;
        }
        List componResultList = new ArrayList();
        List<List<Road>> invokList = this.getExistModeName(recommendContext, handleRuleList);
        if (CollectionUtils.isEmpty(invokList)) {
            return layerResultList;
        }
        List<Road> roadList = getRoadList(invokList);
        if (CollectionUtils.isEmpty(roadList)) {
            return layerResultList;
        }

        String isParallel = recommendContext.getLayer().getIsParallel();
        if ("true".equalsIgnoreCase(isParallel) && !CollectionUtils.isEmpty(roadList) && roadList.size() > 1) {
            doAsyncProcess(recommendContext, roadList, componResultList, layerResultList);
        } else {
            doSyncProcessV2(recommendContext, roadList, componResultList, layerResultList);
        }

        String dpsLogDebug = StringUtils.isEmpty(recommendContext.getLayer().getDpsLogDebug()) ? "false" : recommendContext.getLayer().getDpsLogDebug();
        if (recommendContext.getLayer().getLayerName().equalsIgnoreCase(LayerEnum.POSTRANK.code)) {
            log.info("userId:{},sence:{},group:{},layer:{},size:{},response:{}", recommendContext.getRequest().getUserId(), recommendContext.getRequest().getSence(), recommendContext.getGroupName(), recommendContext.getLayer().getLayerName(), componResultList.size(), JSON.toJSONString(componResultList));
        } else if (dpsLogDebug.equalsIgnoreCase("true")) {
            log.info("userId:{},sence:{},group:{},layer:{},size:{},response:{}", recommendContext.getRequest().getUserId(), recommendContext.getRequest().getSence(), recommendContext.getGroupName(), recommendContext.getLayer().getLayerName(), componResultList.size(), JSON.toJSONString(componResultList));
        }
        return componResultList;
    }


    /**
     * 执行异步分层策略
     *
     * @param recommendContext
     * @param invok
     * @param componList
     * @param list
     */
    private void doAsyncProcess(DpsRequest recommendContext, List<Road> invokList, List componResultList, List layerResultList) {
        List<CompletableFuture<String>> lst = Lists.newArrayListWithCapacity(invokList.size());
        long timeOut = this.getTimeOut(recommendContext);
        Map mdcContextMap = MDC.getCopyOfContextMap();
        List list = Collections.synchronizedList(new ArrayList<>());
        for (Road road : invokList) {
            String modelName = road.getModelName();
            String model = road.getModel();
            ProcessInterface processInterface = processInterfaceMap.get(model);
            DpsRequest context = convertProcess(recommendContext, road);
            lst.add(CompletableFuture.supplyAsync(new Supplier<String>() {
                @Override
                public String get() {
                    Stopwatch timer = Stopwatch.createStarted();
                    try {
                        if (null != mdcContextMap) {
                            MDC.setContextMap(mdcContextMap);
                        }
                        List resultList = dpsProcessor.doAsyncRoadProcess(context, road, processInterface, layerResultList);
                        recommendContext.getExtMap().putAll(context.getExtMap());
                        if (!CollectionUtils.isEmpty(resultList)) {
                            list.addAll(resultList);
                        }
                        timer.stop();
                        log.info("userId:{},sence:{},group:{},layer:{},model:{},modelName:{},modelGroup:{},size:{},time:{}ms", recommendContext.getRequest().getUserId(), recommendContext.getRequest().getSence(), recommendContext.getGroupName(), recommendContext.getLayer().getLayerName(), model, modelName, road.getGroupName(), CollectionUtils.isEmpty(resultList) ? 0 : resultList.size(), timer.elapsed(TimeUnit.MILLISECONDS));
                    } catch (Exception e) {
                        timer.stop();
                        log.error("userid:{},sence:{},group:{},layer:{},model:{},modelName:{},modelGroup:{},time:{}ms,error:{}", recommendContext.getRequest().getUserId(), recommendContext.getRequest().getSence(), recommendContext.getGroupName(), recommendContext.getLayer().getLayerName(), model, modelName, road.getGroupName(), timer.elapsed(TimeUnit.MILLISECONDS), getStackTraceAsString(e));
                    }
                    return "success";
                }
            }, taskExecutor));
        }
        CompletableFuture<Void> featureFetchers = CompletableFuture.allOf(lst.toArray(new CompletableFuture<?>[0]));
        try {
            featureFetchers.get(timeOut + 10, TimeUnit.MILLISECONDS);
            //featureFetchers.get();
        } catch (Exception e) {
            log.error("userId:{},sence:{},group:{},layer:{},timeOut:{}ms,error:{}", recommendContext.getRequest().getUserId(), recommendContext.getRequest().getSence(), recommendContext.getGroupName(), recommendContext.getLayer().getLayerName(), timeOut, getStackTraceAsString(e));
        }
        componResultList.addAll(list);
    }


    public List doAsyncRoadProcess(DpsRequest context, Road road, ProcessInterface processInterface, List layerResultList) {
        layerResultList = CollectionUtils.isEmpty(layerResultList) ? Lists.newArrayList() : layerResultList;
        List resultList = dpsProcessor.doProcessResult(context, road, processInterface, layerResultList);
        return resultList;
    }


    /**
     * 执行串行分层策略
     *
     * @param recommendContext
     * @param invok
     * @param componList
     * @param list
     */
    private void doSyncProcessV2(DpsRequest recommendContext, List<Road> invokList, List componResultList, List layerResultList) {
        final List[] list = {new ArrayList()};
        list[0].addAll(layerResultList);
        for (Road road : invokList) {
            String modelName = road.getModelName();
            String model = road.getModel();
            ProcessInterface processInterface = processInterfaceMap.get(model);
            DpsRequest context = convertProcess(recommendContext, road);
            Stopwatch timer = Stopwatch.createStarted();
            try {
                list[0] = dpsProcessor.doSyncRoadProcess(context, road, processInterface, list);
                recommendContext.getExtMap().putAll(context.getExtMap());
                timer.stop();
                log.info("userId:{},sence:{},group:{},layer:{},model:{},modelName:{},modelGroup:{},size:{},time:{}ms", recommendContext.getRequest().getUserId(), recommendContext.getRequest().getSence(), recommendContext.getGroupName(), recommendContext.getLayer().getLayerName(), model, modelName, road.getGroupName(), CollectionUtils.isEmpty(list[0]) ? 0 : list[0].size(), timer.elapsed(TimeUnit.MILLISECONDS));
            } catch (Exception e) {
                timer.stop();
                log.error("userid:{},sence:{},group:{},layer:{},model:{},modelName:{},modelGroup:{},time:{}ms,error:{}", recommendContext.getRequest().getUserId(), recommendContext.getRequest().getSence(), recommendContext.getGroupName(), recommendContext.getLayer().getLayerName(), model, modelName, road.getGroupName(), timer.elapsed(TimeUnit.MILLISECONDS), getStackTraceAsString(e));
            }
        }
        list[0] = CollectionUtils.isEmpty(list[0]) ? Lists.newArrayList() : list[0];
        componResultList.addAll(list[0]);
    }


    @GwtIncompatible
    public static String getStackTraceAsString(Throwable throwable) {
        StringWriter stringWriter = new StringWriter();
        throwable.printStackTrace(new PrintWriter(stringWriter));
        String string = stringWriter.toString();
        Matcher m = p.matcher(string);
        string = m.replaceAll("");
        return string;
    }


    public List doSyncRoadProcess(DpsRequest context, Road road, ProcessInterface processInterface, List[] list) {
        list[0] = CollectionUtils.isEmpty(list[0]) ? Lists.newArrayList() : list[0];
        list[0] = dpsProcessor.doProcessResult(context, road, processInterface, list[0]);
        return list[0];
    }

    @CustomizedMetrics(customPrefix = "dps_sence_layer_road", tagMapper = DpsRoadTagMapper.class)
    public List doProcessResult(DpsRequest context, Road road, ProcessInterface processInterface, List list) {
        return processInterface.doProcess(context, list);
    }


    /**
     * 加载场景规则
     * ps_sence_layer_timeout
     *
     * @param recommendContext
     * @param layer
     * @return
     */
    private List<List<Road>> dealLayerRule(DpsRequest dpsRequests, Layer layer) {
        if (Objects.isNull(layer)) {
            return Lists.newArrayList();
        }
        DefaultLayerRule defaultLayerRule = new DefaultLayerRule();
        List<List<Road>> handleList = layer.getRoad();
        defaultLayerRule.setProperties(layer.getProperties());
        defaultLayerRule.setLayerName(layer.getLayerName());
        defaultLayerRule.setDpsTimeOut(layer.getDpsTimeOut());
        defaultLayerRule.setIsParallel(layer.getIsParallel());
        defaultLayerRule.setDpsLogDebug(layer.getDpsLogDebug());
        dpsRequests.setLayer(null);
        dpsRequests.setLayer(defaultLayerRule);
        return handleList;
    }


}
